# LeetCode 337、打家劫舍III
# 一、题目描述
在上次打劫完一条街道之后和一圈房屋后,小偷又发现了一个新的可行窃的地区。
这个地区只有一个入口,我们称之为根。
除了“根”之外,每栋房子有且只有一个“父“房子与之相连。
一番侦察之后,聪明的小偷意识到“这个地方的所有房屋的排列类似于一棵二叉树”。 如果两个直接相连的房子在同一天晚上被打劫,房屋将自动报警。计算在不触动警报的情况下,小偷一晚能够盗取的最高金额。
示例 1:
输入: [3,2,3,null,3,null,1]
3
/ \
2 3
\ \
3 1
输出: 7
解释: 小偷一晚能够盗取的最高金额 = 3 + 3 + 1 = 7.
示例 2:
输入: [3,4,5,1,3,null,1]
3
/ \
4 5
/ \ \
1 3 1
输出: 9
解释: 小偷一晚能够盗取的最高金额 = 4 + 5 = 9.
# 二、题目解析
# 三、参考代码
# 1、Java 代码
// 登录 AlgoMooc 官网获取更多算法图解
// https://www.algomooc.com
// 作者:程序员吴师兄
// 代码有看不懂的地方一定要私聊咨询吴师兄呀
// 打家劫舍III( LeetCode 337 ):https://leetcode-cn.com/problems/house-robber-iii/submissions/
class Solution {
public int rob(TreeNode root) {
// 每个节点都有两种选择:偷、不偷
// 从叶子节点开始判断,直到来到根节点
int[] res = chooseNode(root);
// 选择当前节点偷和不偷抉择时的较大值
return Math.max(res[0], res[1]);
}
private int[] chooseNode(TreeNode node) {
// 递归终止条件
// 即在叶子节点的时候,叶子节点的左右子节点为 null
// 继续调用下去就会返回给叶子节点,它的左右子树的选择的金额都是 0
if (node == null) {
return new int[]{0, 0};
}
// 否则的话,说明当前节点有值
// 需要判断,当前节点是偷还是不偷
// 1、如果选择偷,那么左子节点不能偷,这个时候需要获取不偷左子节点时的最大金额
// 2、如果选择偷,那么右子节点不能偷,这个时候需要获取不偷右子节点时的最大金额
// 3、如果选择不偷,那么可以偷左右子节点,这个时候的最大金额为左右子节点最大金额之和
// dp[0] 表示的是以当前 node 为根节点的子树能够偷取的最大金额,并且此时采取【不偷】 node 这个节点的策略
// dp[1] 表示的是以当前 node 为根节点的子树能够偷取的最大金额,并且此时采取【偷】 node 这个节点的策略
int[] dp = new int[2];
// 先去计算左子节点的选择
int[] left = chooseNode(node.left);
// 再去计算右子节点的选择
int[] right = chooseNode(node.right);
// 如果选择不偷,那么可以偷左右子节点,这个时候的最大金额为左右子节点最大金额之和
dp[0] = Math.max(left[0], left[1]) + Math.max(right[0], right[1]);
// 1、如果选择偷,那么左子节点不能偷,这个时候需要获取不偷左子节点时的最大金额
// 2、如果选择偷,那么右子节点不能偷,这个时候需要获取不偷右子节点时的最大金额
// 3、再加上当前节点的金额
dp[1] = left[0] + right[0] + node.val;
return dp;
}
}